Make use of the device camera to take and serialize a photo, display it as thumbnail and create a simple single-photo gallery.
In this lab you will use the device camera to take a photo.
The following figures provide an outline.
Proceeding with the application as completed to the end of the previous lab, we shall add two new activities, the content for which is provided in a later step:
Modify manifest file: add following snippets.
<activity
android:name=".activities.ResidenceCameraActivity"
android:label="@string/app_name">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.ResidencePagerActivity"/>
</activity>
<activity
android:name=".activities.ResidenceGalleryActivity"
android:label="@string/app_name">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.ResidencePagerActivity"/>
</activity>
We add a Camera button and a thumnail placeholder to the Residence details view:
First add 2 string resources:
<string name="take_photo">Take Photo</string>
<string name="save_photo">Save Photo</string>
Here is the additional code:
<!-- Camera button + thumbnail-->
<ImageButton
android:id="@+id/camera_button"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:layout_marginTop="16dp"
android:src="@android:drawable/ic_menu_camera"
android:layout_gravity="top"/>
<ImageView
android:id="@+id/myrent_imageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:background="@android:color/darker_gray"
android:cropToPadding="true"
android:layout_marginTop="16dp"
android:layout_gravity="top|end"
android:layout_margin="16dp"/>
As you can see, the code is located immediately following the geolocation input window.
For reference, here is the refactored fragment_residence.xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- LOCATION -->
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/location" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="51dp"
android:baselineAligned="false"
android:orientation="horizontal"
android:weightSum="1">
<!-- Geolocation (GPS Coords) -->
<EditText
android:id="@+id/geolocation"
android:layout_width="194dp"
android:layout_height="wrap_content"
android:hint="@string/geolocation_hint"
android:layout_weight="0.75">
<requestFocus />
</EditText>
<!-- Camera button + thumbnail-->
<ImageButton
android:id="@+id/camera_button"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:layout_marginTop="16dp"
android:src="@android:drawable/ic_menu_camera"
android:layout_gravity="top"/>
<ImageView
android:id="@+id/myrent_imageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:background="@android:color/darker_gray"
android:cropToPadding="true"
android:layout_marginTop="16dp"
android:layout_gravity="top|end"
android:layout_margin="16dp"/>
</LinearLayout>
<!-- STATUS -->
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/status" />
<Button android:id="@+id/registration_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
<!-- Checkbox -->
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/isrented"
android:id="@+id/isrented"
android:checked="false"/>
<Button
android:id="@+id/tenant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="@string/landlord" />
<Button android:id="@+id/residence_reportButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="@string/residence_report"
/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_blue_marker"/>
</LinearLayout>
Next we add a layout for the Camera activity:
File: /res/layout/residence_photo.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="org.wit.myrent.activities.ResidenceCameraActivity" >
<ImageView
android:id="@+id/residenceImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/takePhoto"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="@mipmap/ic_lens"/>
<Button
android:id="@+id/takePhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/residenceImage"
android:layout_alignParentBottom="true"
android:text="@string/take_photo" />
<Button
android:id="@+id/savePhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/takePhoto"
android:layout_alignRight="@+id/residenceImage"
android:layout_alignTop="@+id/takePhoto"
android:text="@string/save_photo" />
</RelativeLayout>
Download the image ic_lens.png from here to a temp location.
new|Image Asset
.Configure Image Asset
window should open.ic_lens
.If using an Android emulator ensure you enable at least one camera simulator in the settings. This is illustrated in Figure 5.
The model Residence class requires a new field to represent the photo filename:
public String photo;
public Residence()
{
...
photo = "photo";
}
For reference here is the updated Residence:
package org.wit.myrent.models;
import android.content.Context;
import org.wit.myrent.R;
import java.util.Date;
import java.util.Random;
public class Residence
{
public Long id;
public Long date;
//a latitude longitude pair
//example "52.4566,-6.5444"
public String geolocation;
public boolean rented;
public String tenant;
public double zoom ;//zoom level of accompanying map
public String photo;
public Residence() {
id = unsignedLong();
date = new Date().getTime();
geolocation = "52.253456,-7.187162";
tenant = "none presently";
zoom = 16.0f;
photo = "photo";
}
public void setGeolocation(String geolocation) {
this.geolocation = geolocation;
}
public String getGeolocation() {
return geolocation;
}
public String getDateString() {
return "Registered:" + dateString();
}
private String dateString() {
String dateFormat = "EEE d MMM yyyy H:mm";
return android.text.format.DateFormat.format(dateFormat, date).toString();
}
public String getResidenceReport(Context context) {
String rentedString = "";
if (rented) {
rentedString = context.getString(R.string.residence_report_rented);
}
else {
rentedString = context.getString(R.string.residence_report_not_rented);
}
String prospectiveTenant = tenant;
if (tenant == null) {
prospectiveTenant = context.getString(R.string.residence_report_nobody_interested);
}
else {
prospectiveTenant = context.getString(R.string.residence_report_prospective_tenant, tenant);
}
String report = "Location " + geolocation + " Date: " + dateString() + " " + rentedString + " " + prospectiveTenant;
return report;
}
/**
* Generate a long greater than zero
* @return Unsigned Long value greater than zero
*/
private Long unsignedLong() {
long rndVal = 0;
do {
rndVal = new Random().nextLong();
} while (rndVal <= 0);
return rndVal;
}
}
We will now add two helper classes, CameraHelper & FileIOHelper.
CameraHelper class has 2 methods:
These methods have been obtained from Android Programming by Hardy & Phillips.
File: org.wit.android.helpers.CameraHelper.java
package org.wit.android.helpers;
import java.util.List;
import org.wit.myrent.models.Residence;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.hardware.Camera.Size;
import android.view.Display;
import android.widget.ImageView;
public class CameraHelper
{
/**
* Render the photo on the ImageView
*/
public static void showPhoto(Activity activity, Residence res, ImageView photoView)
{
String path = activity.getFileStreamPath(res.photo).getAbsolutePath();
BitmapDrawable b = getScaledDrawable(activity, path);
if (b != null)
photoView.setImageDrawable(b);
}
/**
* Get a BitmapDrawable from a local file that is scaled down to fit the
* current Window size.
*/
@SuppressWarnings("deprecation")
public static BitmapDrawable getScaledDrawable(Activity a, String path)
{
Display display = a.getWindowManager().getDefaultDisplay();
float destWidth = display.getWidth();
float destHeight = display.getHeight();
// read in the dimensions of the image on disk
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;
if (srcHeight > destHeight || srcWidth > destWidth)
{
if (srcWidth > srcHeight)
{
inSampleSize = Math.round((float) srcHeight / (float) destHeight);
}
else
{
inSampleSize = Math.round((float) srcWidth / (float) destWidth);
}
}
options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return new BitmapDrawable(a.getResources(), bitmap);
}
}
Here is the FileIOHelper class:
package org.wit.android.helpers;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import android.content.Context;
import android.graphics.Bitmap;
public class FileIOHelper
{
public static boolean write(Context context, String filename, byte[] data)
{
FileOutputStream os = null;
boolean success = true;
try
{
os = context.openFileOutput(filename, Context.MODE_PRIVATE);
os.write(data);
}
catch (Exception e)
{
LogHelpers.info(context,"Error writing to file " + filename + " " + e.getMessage());
success = false;
}
finally
{
try
{
if (os != null)
os.close();
}
catch (Exception e)
{
LogHelpers.info(context, "Error closing file " + filename + " " + e.getMessage());
success = false;
}
}
return success;
}
public static byte[] byteArray(Bitmap bmp)
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
return stream.toByteArray();
}
public static boolean writeBitmap(Context context, String filename, Bitmap bmp)
{
return write(context, filename, byteArray(bmp));
}
}
Note: Not all necessary import statements are supplied as accompaniment to the various code snipptes below. A complete set of statements is provided at the end of this file for reference purposes.
Create a new activity ResidenceCameraActivity in the package org.wit.myrent.activities.
package org.wit.myrent.activities;
import org.wit.myrent.R;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class ResidenceCameraActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.residence_photo);
}
}
Introduce and wire up the Up button so that it behaves similarly to the back button. We can do this by invoking Activity.onBackPressed
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Add an import statement for MenuItem:
import android.view.MenuItem;
There are two buttons in the view: Take Photo and Save Photo.
private Button savePhoto;
private Button takePhoto;
Import the Button class:
import android.widget.Button;
Add an ImageView field in which the photo will be displayed.
private ImageView residenceImage;
Import the ImageView class:
import android.widget.ImageView;
Bind the widget instances to their respective layout elements. The following code which should be added to onCreate does this:
residenceImage = (ImageView) findViewById(R.id.residenceImage);
savePhoto = (Button)findViewById(R.id.savePhoto);
takePhoto = (Button)findViewById(R.id.takePhoto);
Set the default state of the save photo button to disabled. We will only enable this once a photo has been taken.
savePhoto.setEnabled(false);
Implement an OnClickListener interface:
public class ResidenceCameraActivity extends AppCompatActivity implements OnClickListener
Import the listener interface:
import android.view.View.OnClickListener;
Add the listener method skeleton:
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
}
Register listeners in onCreate to respond to button clicks:
savePhoto.setOnClickListener(this);
takePhoto.setOnClickListener(this);
In onClick method respond to clicking the buttons. For convenience we will locate most of the necessary code, as shown below, in two private methods:
@Override
public void onClick(View v)
{
switch(v.getId())
{
case R.id.takePhoto : onTakePhotoClicked(v);
break;
case R.id.savePhoto : onPictureTaken(residencePhoto);
break;
}
}
The approach we adopt is to use an implicit Intent to make use of the device camera (which we check is available). Here is the implementation of onTakePhotoClicked:
public void onTakePhotoClicked(View v)
{
// Check for presence of device camera. If not present advise user and quit method.
PackageManager pm = getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "Camera app not present on this device", Toast.LENGTH_SHORT).show();
return;
}
// The device has a camera app ... so use it.
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent,CAMERA_RESULT);
savePhoto.setEnabled(true);
}
This requires an import statement for the Intent class:
import android.content.Intent;
Also necessary is a definition of the constant CAMERA_RESULT. The value 5 has been chosen arbitrarily.
private static final int CAMERA_RESULT = 5;
public static final String EXTRA_PHOTO_FILENAME = "org.wit.myrent.photo.filename";
Next we define the method onPictureTaken. We are chosing the png format as this is the only format acceptable to the helper method writeBitMap.
private void onPictureTaken(Bitmap data) {
String filename = UUID.randomUUID().toString() + ".png";
if (writeBitmap(this, filename, data) == true) {
Intent intent = new Intent();
intent.putExtra(EXTRA_PHOTO_FILENAME, filename);
setResult(Activity.RESULT_OK, intent);
}
else {
setResult(Activity.RESULT_CANCELED);
}
finish();
}
This requires these import statements:
import android.graphics.Bitmap;
import static org.wit.android.helpers.FileIOHelper.writeBitmap;
We must also define the string EXTRA_PHOTO_FILENAME. Note that the arguments in Intent.putExtra comprise key-value pairs, the first argument always being a String.
public static final String EXTRA_PHOTO_FILENAME = "org.wit.myrent.photo.filename";
A further instance variable is required to hold the picture in bitmap form:
private Bitmap residencePhoto;
We retrieve the photo taken by the device camera in the onActivityResult method:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode)
{
case ResidenceCameraActivity.CAMERA_RESULT :
if(data != null) {
processImage(data);
}
else {
Toast.makeText(this, "Camera failure: check simulated camera present emulator advanced settings",
Toast.LENGTH_LONG).show();
}
break;
}
}
For convenience, we have located the code in a private method, processImage:
private void processImage(Intent data)
{
residencePhoto = (Bitmap) data.getExtras().get("data");
if(residencePhoto == null)
{
Toast.makeText(this, "Attempt to take photo did not succeed", Toast.LENGTH_SHORT).show();
}
residenceImage.setImageBitmap(residencePhoto);
}
Complete set import statements:
import org.wit.myrent.R;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
Here we provide instructions on how to wire up the camera module to the ResidenceFragment class.
Add imports:
import static org.wit.android.helpers.CameraHelper.showPhoto;
import android.widget.ImageView;
Add a static constant to represent the photo:
private static final int REQUEST_PHOTO = 0;
Create instance variables:
private ImageView cameraButton;
private ImageView photoView;
Invoke the showPhoto method in onStart.
showPhoto(getActivity(), residence, photoView);
Here, for reference, is the refactored onStart method:
@Override
public void onStart()
{
super.onStart();
//display thumbnail photo
showPhoto(getActivity(), residence, photoView);
}
In addListeners:
cameraButton = (ImageView) v.findViewById(R.id.camera_button);
photoView = (ImageView) v.findViewById(R.id.myrent_imageView);
cameraButton.setOnClickListener(this);
In onActivityResult introduce code to:
Observe that we are replacing the if-else structure with a switch statement:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (resultCode != Activity.RESULT_OK)
{
return;
}
switch (requestCode)
{
case REQUEST_CONTACT:
String name = ContactHelper.getContact(getActivity(), data);
residence.tenant = name;
tenantButton.setText(name);
break;
case REQUEST_PHOTO:
String filename = data.getStringExtra(ResidenceCameraActivity.EXTRA_PHOTO_FILENAME);
if (filename != null)
{
residence.photo = filename;
showPhoto(getActivity(), residence, photoView );
}
break;
}
}
Respond to camera button click: add this code to onClick:
case R.id.camera_button:
Intent ic = new Intent(getActivity(), ResidenceCameraActivity.class);
startActivityForResult(ic, REQUEST_PHOTO);
break;
Add a new layout file for the gallery:
File: res/layout/residence_gallery.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/FrameLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="org.wit.myrent.activities.ResidenceCameraActivity" >
<ImageView
android:id="@+id/residenceGalleryImage"
android:layout_width="match_parent"
android:layout_height="426dp"
android:src="@mipmap/ic_launcher" />
</FrameLayout>
Here is the activity code:
File: org.wit.myrent.activities/ResidenceGalleryActivity.java
package org.wit.myrent.activities;
import org.wit.myrent.R;
import org.wit.myrent.app.MyRentApp;
import org.wit.myrent.models.Portfolio;
import org.wit.myrent.models.Residence;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.ImageView;
import static org.wit.android.helpers.CameraHelper.showPhoto;
public class ResidenceGalleryActivity extends AppCompatActivity
{
public static final String EXTRA_PHOTO_FILENAME = "org.wit.myrent.photo.filename";
private ImageView photoView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.residence_gallery);
photoView = (ImageView) findViewById(R.id.residenceGalleryImage);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
showPicture();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home : onBackPressed();
return true;
default : return super.onOptionsItemSelected(item);
}
}
private void showPicture()
{
Long resId = (Long)getIntent().getSerializableExtra(ResidenceFragment.EXTRA_RESIDENCE_ID);
MyRentApp app = (MyRentApp) getApplication();
Portfolio portfolio = app.portfolio;
Residence residence = portfolio.getResidence(resId);
showPhoto(this, residence, photoView);
}
}
The gallery activity is started by a long-press of the thumbnail in the residence fragment. Here are the necessary changes to ResidenceFragment:
OnLongClickListener
interface to those already implemented, the purpose being to listen for a long-press on the thumbnail image.
public class ResidenceFragment extends Fragment implements
TextWatcher,
OnCheckedChangeListener,
OnClickListener,
DatePickerDialog.OnDateSetListener,
View.OnLongClickListener
photoView.setOnLongClickListener(this);
/* ====================== longpress thumbnail ===================================*/
/*
* Long press the bitmap image to view photo in single-photo gallery
*/
@Override
public boolean onLongClick(View v)
{
Intent i = new Intent(getActivity(), ResidenceGalleryActivity.class);
i.putExtra(EXTRA_RESIDENCE_ID, residence.id);
startActivity(i);
return true;
}
For reference, here is the refactored class:
package org.wit.myrent.activities;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.wit.android.helpers.ContactHelper;
import org.wit.android.helpers.MapHelper;
import org.wit.myrent.R;
import org.wit.myrent.app.MyRentApp;
import org.wit.myrent.models.Portfolio;
import org.wit.myrent.models.Residence;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.app.DatePickerDialog;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.DatePicker;
import android.widget.EditText;
import static org.wit.android.helpers.ContactHelper.sendEmail;
import static org.wit.android.helpers.IntentHelper.navigateUp;
import static org.wit.android.helpers.IntentHelper.startActivityWithData;
import static org.wit.android.helpers.CameraHelper.showPhoto;
import android.widget.ImageView;
public class ResidenceFragment extends Fragment implements TextWatcher,
OnCheckedChangeListener,
OnClickListener,
DatePickerDialog.OnDateSetListener,
View.OnLongClickListener
{
public static final String EXTRA_RESIDENCE_ID = "myrent.RESIDENCE_ID";
private static final int REQUEST_CONTACT = 1;
private static final int REQUEST_PHOTO = 0;
private EditText geolocation;
private CheckBox rented;
private Button dateButton;
private Button tenantButton;
private Button reportButton;
private Residence residence;
private Portfolio portfolio;
String emailAddress = "";
private ImageView cameraButton;
private ImageView photoView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
UUID resId = (UUID) getActivity().getIntent().getSerializableExtra(EXTRA_RESIDENCE_ID);
MyRentApp app = (MyRentApp) getActivity().getApplication();
portfolio = app.portfolio;
residence = portfolio.getResidence(resId);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
super.onCreateView(inflater, parent, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_residence, parent, false);
addListeners(v);
updateControls(residence);
FloatingActionButton fab = (FloatingActionButton) v.findViewById(R.id.fab);
fab.setOnClickListener(this);
return v;
}
private void addListeners(View v) {
geolocation = (EditText) v.findViewById(R.id.geolocation);
dateButton = (Button) v.findViewById(R.id.registration_date);
rented = (CheckBox) v.findViewById(R.id.isrented);
tenantButton = (Button) v.findViewById(R.id.tenant);
reportButton = (Button) v.findViewById(R.id.residence_reportButton);
cameraButton = (ImageView) v.findViewById(R.id.camera_button);
photoView = (ImageView) v.findViewById(R.id.myrent_imageView);
geolocation.addTextChangedListener(this);
dateButton.setOnClickListener(this);
rented.setOnCheckedChangeListener(this);
tenantButton.setOnClickListener(this);
reportButton.setOnClickListener(this);
cameraButton.setOnClickListener(this);
photoView.setOnLongClickListener(this);
}
public void updateControls(Residence residence) {
geolocation.setText(residence.geolocation);
rented.setChecked(residence.rented);
dateButton.setText(residence.getDateString());
}
@Override
public void onStart() {
super.onStart();
//display thumbnail photo
showPhoto(getActivity(), residence, photoView);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
navigateUp(getActivity());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onPause() {
super.onPause();
portfolio.saveResidences();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
switch (requestCode) {
case REQUEST_CONTACT:
String name = ContactHelper.getContact(this, data);
emailAddress = ContactHelper.getEmail(this, data);
tenantButton.setText(name + " : " + emailAddress);
residence.tenant = name;
break;
case REQUEST_PHOTO:
String filename = data.getStringExtra(ResidenceCameraActivity.EXTRA_PHOTO_FILENAME);
if (filename != null) {
residence.photo = filename;
showPhoto(getActivity(), residence, photoView);
}
break;
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable c) {
Log.i(this.getClass().getSimpleName(), "geolocation " + c.toString());
residence.geolocation = c.toString();
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
residence.rented = isChecked;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.registration_date:
Calendar c = Calendar.getInstance();
DatePickerDialog dpd = new DatePickerDialog(getActivity(), this, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
dpd.show();
break;
case R.id.tenant:
Intent i = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(i, REQUEST_CONTACT);
if (residence.tenant != null) {
tenantButton.setText("Tenant: " + residence.tenant);
}
break;
case R.id.residence_reportButton:
sendEmail(getActivity(), "emailAddress", getString(R.string.residence_report_subject), residence.getResidenceReport(getActivity()));
break;
case R.id.fab:
startActivityWithData(getActivity(), MapActivity.class, EXTRA_RESIDENCE_ID, residence.id);
break;
case R.id.camera_button:
Intent ic = new Intent(getActivity(), ResidenceCameraActivity.class);
startActivityForResult(ic, REQUEST_PHOTO);
break;
}
}
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
Date date = new GregorianCalendar(year, monthOfYear, dayOfMonth).getTime();
residence.date = date;
dateButton.setText(residence.getDateString());
}
/* ====================== longpress thumbnail ===================================*/
/*
* Long press the bitmap image to view photo in single-photo gallery
*/
@Override
public boolean onLongClick(View v)
{
Intent i = new Intent(getActivity(), ResidenceGalleryActivity.class);
i.putExtra(EXTRA_RESIDENCE_ID, residence.id);
startActivity(i);
return true;
}
}
Test the app as follows:
The application at the end of this lab is available for reference here: myrent-15
This file contains the gist of the code required to determine if permission to use the camera is to be allowed or denied at runtime.
package org.wit.mytweet.activities;
import android.support.v4.app.ActivityCompat;
...
...
public class MyTweetCameraActivity extends AppCompatActivity implements View.OnClickListener {
public static final int CAMERA_PERMISSION = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
@Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.takePhoto:
/*-- jf 24-12-2015--*/
checkCameraPermission();
//onTakePhotoClicked(v);
break;
...
...
}
}
public void onTakePhotoClicked() /*-- jf 24-12-2015-- removed redundant parameter*/
{
...
...
}
/*-- jf 24-12-2015--*/
/**
* http://stackoverflow.com/questions/32714787/android-m-permissions-onrequestpermissionsresult-not-being-called
* This is an override of AppCompat.onRequestPermissionsResult
*
* @param requestCode
* @param permissions String array of permissions requested.
* @param grantResults int array of results for permissions request.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MyTweetCameraActivity.CAMERA_RESULT : {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onTakePhotoClicked();
}
break;
}
}
}
/**
* Bespoke method to check if camera permission exists.
* If it exists then the photo taken.
* Otherwise, the method AppCompat.request permissions is invoked and
* The response is via the callback onRequestPermissionsResult.
* In onRequestPermissionsResult, on successfully being granted permission photo taken.
* See: https://developer.android.com/training/permissions/requesting.html
* See: http://bit.ly/2hBUAZD
*/
private void checkCameraPermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
onTakePhotoClicked();
}
else {
// Invoke callback to request user-granted permission
ActivityCompat.requestPermissions( // If this file represents fragment replace ActivityCompat with android.support.v13.app.FragmentCompat;
new String[]{Manifest.permission.CAMERA},
CAMERA_PERMISSION);
}
}
/*-- jf 24-12-2015--end--*/
}
When a photo is taken and saved:
When a residence is deleted, delete the photo on the server.